using System;
using Zsa.Emulation.Mappers;
using Zsa.Emulation.Memory;
using Zsa.Emulation.PPU;

namespace Zsa.Emulation.Cartridge
{
    /// <summary>
    /// The cartridge slot.
    /// </summary>
    public class CartridgeSlot
    {
        #region Fields
        /// <summary>
        /// The mapper;
        /// </summary>
        private Mapper _Mapper;

        /// <summary>
        /// The current cart.
        /// </summary>
        private Cart _Cart = null;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="CartIO"/> class.
        /// </summary>
        public CartridgeSlot()
        {
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Inits this instance.
        /// </summary>
        public void Init()
        {
            if (IsEmpty)
            {
                throw new CartNotInsertedException();
            }
            _Mapper.Init(_Cart);
        }

        /// <summary>
        /// Loads the cart.
        /// </summary>
        /// <param name="fileInfo">The file info.</param>
        public void LoadCart(CartridgeFileInfo fileInfo)
        {
            LoadCart(DumpManager.LoadCart(fileInfo.FullPath));
        }

        /// <summary>
        /// Loads the cart.
        /// </summary>
        /// <param name="cart">The cart.</param>
        public void LoadCart(Cart cart)
        {
            if (cart == null)
            {
                throw new ArgumentNullException("cart", "The cart cannot be null !");
            }
            // Create the mapper.
            Mapper _MapperTemp = MapperFactory.CreateMapper(cart.FileInfo);

            _Mapper = _MapperTemp;
            _Cart = cart;
        }

        /// <summary>
        /// Removes the cart.
        /// </summary>
        public void RemoveCart()
        {
            _Cart = null;
            _Mapper = null;
        }
        #endregion Methods

        #region Properties
        /// <summary>
        /// Gets the current cart.
        /// </summary>
        /// <value>The current cart.</value>
        public Cart CurrentCart
        {
            get
            {
                if (_Cart == null)
                {
                    throw new CartNotInsertedException();
                }
                return _Cart;
            }
        }
        public Cartridge.CartridgeFileInfo CartridgeFileInfo
        {
            get
            {
                if (_Cart == null)
                {
                    throw new CartNotInsertedException();
                }
                return _Cart.FileInfo;
            }
        }
        /// <summary>
        /// Gets the current mapper.
        /// </summary>
        /// <value>The current mapper.</value>
        public Mapper CurrentMapper
        {
            get
            {
                if (_Mapper == null)
                {
                    throw new CartNotInsertedException();
                }
                return _Mapper;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is empty.
        /// </summary>
        /// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value>
        public bool IsEmpty
        {
            get
            {
                return _Cart == null;
            }
        }

        #region Memory 
        /// <summary>
        /// Gets the program memory.
        /// </summary>
        /// <value>The program memory.</value>
        public MemoryItem[] ProgramMemory
        {
            get
            {
                return new MemoryItem[]
                {
                    ProgramMemoryBankOne,
                    ProgramMemoryBankTwo
                };
            }
        }

        /// <summary>
        /// Gets the program memory bank one.
        /// </summary>
        /// <value>The program memory bank one.</value>
        public MemoryItem ProgramMemoryBankOne
        {
            get
            {
                return 
                    new MemoryItem(
                        _Mapper.PrgRomBankOne,
                        new MemoryBlock(
                            NesMemoryConstants.PrgRomStart,
                            NesMemoryConstants.PrgRomBankByteSize
                            )
                        );
            }
        }

        /// <summary>
        /// Gets the program memory bank two.
        /// </summary>
        /// <value>The program memory bank two.</value>
        public MemoryItem ProgramMemoryBankTwo
        {
            get
            {
                return new MemoryItem(
                        _Mapper.PrgRomBankTwo,
                        new MemoryBlock(
                            NesMemoryConstants.PrgRomBankTwoStart, 
                            NesMemoryConstants.PrgRomBankByteSize
                    )
                );
            }
        }

        /// <summary>
        /// Gets the video memory.
        /// </summary>
        /// <value>The video memory.</value>
        public MemoryItem[] VideoMemory
        {
            get
            {
                return new MemoryItem[]
                {
                   VideoMemoryBankOne,
                    VideoMemoryBankTwo
                };
            }
        }

            /// <summary>
            /// Gets the video memory bank one.
            /// </summary>
            /// <value>The video memory bank one.</value>
        public MemoryItem VideoMemoryBankOne
        {
            get
            {
                return new MemoryItem(
                        _Mapper.ChrRomOne,
                        new MemoryBlock(0, VideoMemoryConstants.PatternTableByteSize));
            }
        }

        /// <summary>
        /// Gets the video memory bank two.
        /// </summary>
        /// <value>The video memory bank two.</value>
        public MemoryItem VideoMemoryBankTwo
        {
            get
            {
                return new MemoryItem(
                        _Mapper.ChrRomTwo,
                        new MemoryBlock(VideoMemoryConstants.PatternTableByteSize, VideoMemoryConstants.PatternTableByteSize, VideoMemoryConstants.PatternTableByteSize));
            }
        }
        #endregion Memory
        #endregion Properties
    }
}
